#include "hardwareinterface.hpp"

HardwareInterface::HardwareInterface(DataMemoryOrganisation *dmo, QObject *parent) :
    QObject(parent), cr('\x0D')
{
    this->dataMemoryOrganisation = dmo;

    this->settings = AppSettings::getInstance();

    this->serialPort = NULL;
    this->initialized = false;
    this->currentState = ENDREAD;
    this->readBuffer = "";
}


HardwareInterface::~HardwareInterface()
{
    this->settings = NULL;
    AppSettings::releaseInstance();
    delete this->dataMemoryOrganisation;
    delete this->serialPort;
}


void HardwareInterface::processSerialPortQuery()
{
    if (!this->initialized || this->currentState != ENDREAD)
	return;

    // Get Registers to send
    int regToSend[4];
    regToSend[0] = this->dataMemoryOrganisation->getPortA()->getTrisValue();
    regToSend[1] = this->dataMemoryOrganisation->getPortB()->getTrisValue();
    regToSend[2] = this->dataMemoryOrganisation->getPortA()->getValue();
    regToSend[3] = this->dataMemoryOrganisation->getPortB()->getValue();

    // Build up the message data
    char sendBuffer[8];
    char blub = (char) 0x30;
    for (int i = 0; i < 4; i++)
    {
	sendBuffer[i * 2] = (char) ((regToSend[i] & 0xF0) >> 4) | 0x30;
	sendBuffer[(i * 2) + 1] = (char) (regToSend[i] & 0x0F) | 0x30;
    }

    // Send
    QString buffer;
    for (int i = 0; i < 8; i++)
    {
	this->serialPort->write(&sendBuffer[i]);
	buffer.append(sendBuffer[i]);
    }

    this->serialPort->write(&this->cr); //Append Carriage Return
    this->currentState = STARTREAD;
    this->serialPort->flush();
    emit dataWritten(buffer);
}


void HardwareInterface::readFromSerialPort()
{
    // Check if all bytes are available
    if (this->serialPort->bytesAvailable() != 17 || !this->initialized)
	return;

    this->readPortA = 0;
    this->readPortB = 0;

    char buffer[8];

    // Split Input into the needed parts
    while (this->serialPort->bytesAvailable() >= 8)
    {
	this->serialPort->read(buffer, 8);

	switch (this->currentState)
	{
	case STARTREAD:
	    this->readBuffer.clear();
	    this->readBuffer.append(buffer);
	    this->readPortA = (((int) buffer) & 0x0F) << 4;
	    this->currentState = READPORTA_1;
	    break;

	case READPORTA_1:
	    this->readBuffer.append(buffer);
	    this->readPortA |= ((int) buffer) & 0x0F;
	    this->currentState = READPORTA_2;
	    break;

	case READPORTA_2:
	    this->readBuffer.append(buffer);
	    this->readPortB = (((int) buffer) & 0x0F) << 4;
	    this->currentState = READPORTB_1;
	    break;

	case READPORTB_1:
	    this->readBuffer.append(buffer);
	    this->readPortB |= ((int) buffer) & 0x0F;
	    this->currentState = READPORTB_2;
	    break;

	case READPORTB_2:
	    qDebug() << "State ReadPortB2 reached.. This should not happen..";
	    break;

	default:
	    qDebug() << "Current State unknown.. " << buffer;
	    break;
	}
    }

    // Check for Carriage Return at the End of Data
    char cr;
    this->serialPort->read(&cr, 1);
    if (this->currentState == READPORTB_2 && cr == this->cr)
    {
	this->readBuffer.append(buffer);
	this->currentState = ENDREAD;
	emit informationAvailable();
    }
}


void HardwareInterface::processInformation()
{
    if (!this->initialized || this->currentState != ENDREAD)
	return;

    // Write the data into the registers
    this->dataMemoryOrganisation->getPortA()->setValue(this->readPortA, true);
    this->dataMemoryOrganisation->getPortB()->setValue(this->readPortB, true);
    emit dataRead(this->readBuffer);
}


void HardwareInterface::onDsrChanged(bool status)
{
    if (status)
	qDebug() << "Device was turned on";
    else
	qDebug() << "Device was turned off";
}


void HardwareInterface::setSerialPort(QString name)
{
    if (this->initialized)
    {
	this->serialPort->close();
	delete this->serialPort;
	this->initialized = false;
    }

    this->serialPort = new AbstractSerial(this);
    // Connect needed Signals
    connect(this->serialPort, SIGNAL(readyRead()), this, SLOT(readFromSerialPort()));

    // Initialize
    this->serialPort->setDeviceName(name);
    if (this->serialPort->open(QIODevice::ReadWrite | QIODevice::Unbuffered))
    {
	// Configure QextSerialPort
	this->serialPort->setBaudRate(AbstractSerial::BaudRate4800);
	this->serialPort->setDataBits(AbstractSerial::DataBits8);
	this->serialPort->setFlowControl(AbstractSerial::FlowControlOff);
	this->serialPort->setParity(AbstractSerial::ParityNone);
	this->serialPort->setStopBits(AbstractSerial::StopBits1);

	this->initialized = true;
	emit ready();
    } else
	emit error(this->serialPort->deviceName(), this->serialPort->errorString());
}
